﻿namespace NetFx.SMIL.Definitions
{
  using System;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;
  using System.Security;
  using System.Security.Permissions;

  using NetFx.MSIL;
  using NetFx.SMIL.References;

  public class TypeDef : MemberDef
  {
    #region Statics
    public static TypeDef of(string name)
    {
      return new TypeDef(name);
    }
    public static TypeDef of(Type type)
    {
      return new TypeDef(type);
    }
    #endregion
    #region Fields
    protected TypeAttributes attributes;
    protected PackingSize packingSize = PackingSize.Unspecified;
    protected TypeRef baseType = null;
    protected List<TypeRef> interfaces = null;
    protected List<FieldDef> fields = null;
    protected List<MethodDef> methods = null;
    protected List<TypeDef> types = null;
    protected List<PropertyDef> properties = null;
    protected List<EventDef> events = null;
    #endregion
    #region Properties
    public override DefType DefinitionType
    {
      get { return DefType.Type; }
    }
    public virtual TypeRef BaseType
    {
      get
      {
        return this.baseType;
      }
      set
      {
        this.baseType = value;
      }
    }
    public virtual TypeAttributes Attributes
    {
      get
      {
        return this.attributes;
      }
      set
      {
        this.attributes = value;
      }
    }
    public virtual PackingSize PackingSize
    {
      get
      {
        return this.packingSize;
      }
      set
      {
        this.packingSize = value;
      }
    }
    public virtual List<TypeRef> Interfaces
    {
      get
      {
        return this.interfaces == null ?
          this.interfaces = new List<TypeRef>() :
          this.interfaces;
      }
    }
    public virtual List<FieldDef> Fields
    {
      get
      {
        return this.fields == null ?
          this.fields = new List<FieldDef>() :
          this.fields;
      }
    }
    public virtual List<MethodDef> Methods
    {
      get
      {
        return this.methods == null ?
          this.methods = new List<MethodDef>() :
          this.methods;
      }
    }
    public virtual List<TypeDef> Types
    {
      get
      {
        return this.types == null ?
          this.types = new List<TypeDef>() :
          this.types;
      }
    }
    public virtual List<PropertyDef> Properties
    {
      get
      {
        return this.properties == null ?
          this.properties = new List<PropertyDef>() :
          this.properties;
      }
    }
    public virtual List<EventDef> Events
    {
      get
      {
        return this.events == null ?
          this.events = new List<EventDef>() :
          this.events;
      }
    }
    #endregion
    #region Constructors
    public TypeDef(string name)
      : base(name)
    {

    }
    public TypeDef(string name, Type parent)
      : base(name)
    {
      this.BaseType = TypeRef.of(parent);
    }
    public TypeDef(string name, Type parent, params Type[] interfaceTypes)
      : this(name, parent)
    {
      if (interfaceTypes != null)
      {
        this.ReloadMembers(interfaceTypes, this.Interfaces);
      }
    }
    public TypeDef(string name, Type parent, PackingSize packingSize)
      : this(name, parent)
    {
      this.PackingSize = packingSize;
    }
    public TypeDef(Type type):base(String.Empty)
    {
      this.OnReflecting(type);
    }
    #endregion
    #region Methods
    #region Defining
    public virtual EventDef DefineEvent(string name)
    {
      return this.AddEvent(EventDef.of(name));
    }
    public virtual EventDef FindEvent(string name)
    {
      if (!string.IsNullOrEmpty(name) && this.events != null)
      {
        foreach (EventDef _event in this.events)
        {
          if (_event != null && _event.Name == name)
          {
            return _event;
          }
        }
      }
      return null;
    }
    public virtual EventDef RemoveEvent(string name)
    {
      return this.RemoveEvent(this.FindEvent(name));
    }
    public virtual PropertyDef DefineProperty(string name)
    {
      return this.AddProperty(PropertyDef.of(name));
    }
    public virtual PropertyDef FindProperty(string name)
    {
      if (!string.IsNullOrEmpty(name) && this.properties != null)
      {
        foreach (PropertyDef property in this.properties)
        {
          if (property != null && property.Name == name)
          {
            return property;
          }
        }
      }
      return null;
    }
    public virtual PropertyDef RemoveProperty(string name)
    {
      return this.RemoveProperty(this.FindProperty(name));
    }
    public virtual FieldDef DefineField(string name)
    {
      return this.AddField(FieldDef.of(name));
    }
    public virtual FieldDef FindField(string name)
    {
      if (!string.IsNullOrEmpty(name) && this.fields != null)
      {
        foreach (FieldDef field in this.fields)
        {
          if (field != null && field.Name == name)
          {
            return field;
          }
        }
      }
      return null;
    }
    public virtual FieldDef RemoveField(string name)
    {
      return this.RemoveField(this.FindField(name));
    }
    public virtual MethodDef DefineMethod(string name)
    {
      return this.AddMethod(MethodDef.of(name));
    }
    public virtual MethodDef FindMethod(string name)
    {
      if (!string.IsNullOrEmpty(name) && this.methods != null)
      {
        foreach (MethodDef method in this.methods)
        {
          if (method != null && method.Name == name)
          {
            return method;
          }
        }
      }
      return null;
    }
    public virtual MethodDef RemoveMethod(string name)
    {
      return this.RemoveMethod(this.FindMethod(name));
    }
    public virtual TypeDef DefineType(string name)
    {
      return this.AddType(TypeDef.of(name));
    }
    public virtual TypeDef FindType(string name)
    {
      if (!string.IsNullOrEmpty(name) && this.types != null)
      {
        foreach (TypeDef type in this.types)
        {
          if (type != null && type.Name == name)
          {
            return type;
          }
        }
      }
      return null;
    }
    public virtual TypeDef RemoveType(string name)
    {
      return this.RemoveType(this.FindType(name));
    }
    public virtual TypeRef AddInterfaceRef(TypeRef interfaceType)
    {
      if (interfaceType != null)
      {
        this.Interfaces.Add(interfaceType);
      }
      return interfaceType;
    }
    public virtual TypeRef RemoveInterfaceRef(TypeRef interfaceType)
    {
      if (interfaceType != null)
      {
        this.Interfaces.Remove(interfaceType);
      }
      return interfaceType;
    }
    #endregion
    #region Helpers
    protected virtual EventDef AddEvent(EventDef _event)
    {
      if (_event != null)
      {
        _event.Parent = this;
        this.Events.Add(_event);
      }
      return _event;
    }
    protected virtual EventDef RemoveEvent(EventDef _event)
    {
      if (_event != null)
      {
        _event.Parent = null;
        this.Events.Remove(_event);
      }
      return _event;
    }
    protected virtual PropertyDef AddProperty(PropertyDef property)
    {
      if (property != null)
      {
        property.Parent = this;
        this.Properties.Add(property);
      }
      return property;
    }
    protected virtual PropertyDef RemoveProperty(PropertyDef property)
    {
      if (property != null)
      {
        property.Parent = null;
        this.Properties.Remove(property);
      }
      return property;
    }
    protected virtual FieldDef AddField(FieldDef field)
    {
      if (field != null)
      {
        field.Parent = this;
        this.Fields.Add(field);
      }
      return field;
    }
    protected virtual FieldDef RemoveField(FieldDef field)
    {
      if (field != null)
      {
        field.Parent = null;
        this.Fields.Remove(field);
      }
      return field;
    }
    protected virtual MethodDef AddMethod(MethodDef method)
    {
      if (method != null)
      {
        method.Parent = this;
        this.Methods.Add(method);
      }
      return method;
    }
    protected virtual MethodDef RemoveMethod(MethodDef method)
    {
      if (method != null)
      {
        method.Parent = null;
        this.Methods.Remove(method);
      }
      return method;
    }
    protected virtual TypeDef AddType(TypeDef type)
    {
      if (type != null)
      {
        type.Parent = this;
        this.Types.Add(type);
      }
      return type;
    }
    protected virtual TypeDef RemoveType(TypeDef type)
    {
      if (type != null)
      {
        type.Parent = null;
        this.Types.Remove(type);
      }
      return type;
    }


    protected override AnyGenerator OnCompleting(AnyGenerator generator)
    {
      TypeGenerator typeGenerator = generator as TypeGenerator;
      if (typeGenerator != null)
      {
        if (this.BaseType != null)
        {
          typeGenerator.ParentType = this.BaseType.ReflectInfo;
        }
        else
        {
          typeGenerator.ParentType = typeof(object);
        }
        if (this.Interfaces.Count > 0)
        {
          foreach (TypeRef interfaceType in this.Interfaces)
          {
            typeGenerator.AddInterfaceImplementation(interfaceType.ReflectInfo);
          }
        }
      }
      return base.OnCompleting(generator);  
    }
    protected override AnyGenerator OnDefining(AnyGenerator container)
    {
      if (container is TypeContainer)
      {
        return (this.PackingSize == PackingSize.Unspecified)
          ? ((TypeContainer)container).DefineType(this.Name, this.Attributes)
          : ((TypeContainer)container).DefineType(this.Name, this.Attributes, null, this.PackingSize)
          ;
      }
      return base.OnDefining(container);
    }
    protected override ReflectDef OnReflecting(object source)
    {
      if (source is Type)
      {
        Type type = source as Type;

        this.attributes = type.Attributes;
        this.baseType = TypeRef.of(type.BaseType);
        if (type is TypeBuilder)
        {
          TypeBuilder tb = type as TypeBuilder;
          this.packingSize = tb.PackingSize;
        }

        this.ReloadMembers(type.GetInterfaces(), this.Interfaces);
        this.ReloadMembers(type.GetFields(AllAccessFlags), this.Fields);
        this.ReloadMembers(type.GetMethods(AllAccessFlags), this.Methods);
        this.ReloadMembers(type.GetNestedTypes(AllAccessFlags), this.Types);
      }
      return base.OnReflecting(source);
    }
    protected override void DisposeList(bool disposing)
    {
      this.DisposeList(this.Fields);
      this.DisposeList(this.Methods);
      this.DisposeList(this.Types);
      this.DisposeList(this.Interfaces);
    }
    protected override void OnBreakingReference()
    {
      this.Break(this.Interfaces);
      this.Break(this.BaseType);
      this.Break(this.Fields);
      this.Break(this.Methods);
      this.Break(this.Types);
      base.OnBreakingReference();
    }
    #endregion
    #endregion
  }
}
